/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "topology/topology_discovery.h"
#include <mc/dbus/shm/serialize.h>
#include <mc/exception.h>
#include <mc/log.h>
#include <vector>
#include <algorithm>
#include <boost/asio.hpp>
#include <thread>

namespace devmon {

std::once_flag topology_discovery::m_once_flag;

topology_discovery& topology_discovery::get_instance(mc::engine::service* service) {
    static std::unique_ptr<topology_discovery> instance;
    
    std::call_once(m_once_flag, [&]() {
        if (service == nullptr) {
            MC_THROW(mc::invalid_arg_exception, "service parameter cannot be empty, must provide a valid service when calling for the first time", 
                     ("service", "nullptr"));
        }
        instance = std::unique_ptr<topology_discovery>(new topology_discovery(service));
    });
    
    return *instance;
}

topology_discovery::~topology_discovery() {
    cleanup();
}

void topology_discovery::cleanup() {
    ilog("cleaning up topology discovery resources");
    
    // 标记为非活跃状态，阻止新的操作
    m_is_active.store(false, std::memory_order_release);
    
    // 等待正在进行的操作完成
    std::lock_guard<std::mutex> lock(m_topology_mutex);
    
    // 清理服务指针
    m_service = nullptr;
    
    ilog("topology discovery cleanup completed");
}

bool topology_discovery::is_service_valid() const {
    // 检查对象是否处于活跃状态以及服务指针是否有效
    return m_is_active.load(std::memory_order_acquire) && m_service != nullptr;
}

void topology_discovery::normal_discovery() {
    if (!is_service_valid()) {
        wlog("topology discovery is not active or service is invalid, skipping normal_discovery");
        return;
    }
    
    ilog("starting topology discovery process");

    try {
        // 启动信号监听
        listen_discovery_signal();
    } catch (const std::exception& e) {
        wlog("failed to setup signal listening, continuing without it: ${error}", ("error", e.what()));
    }

    try {
        // 获取现有对象
        fetch_objects();
    } catch (const std::exception& e) {
        wlog("failed to fetch objects, continuing: ${error}", ("error", e.what()));
    }
    ilog("topology discovery process started successfully");
}

void topology_discovery::listen_discovery_signal() {
    if (!is_service_valid()) {
        return;
    }
    
    try {
        mc::dbus::match_rule rule = mc::dbus::match_rule::new_signal(
            mc::dbus::INTERFACES_ADDED_MEMBER, mc::dbus::DBUS_OBJECT_MANAGER_INTERFACE);
        rule.with_path_namespace("/bmc/kepler/ObjectGroup");
        
        m_service->add_match(rule, [this](mc::dbus::message& msg) {
            if (!is_service_valid()) {
                return; // 如果服务已失效，直接返回
            }
            
            try {
                auto reader = msg.reader();
                mc::dbus::path object_group_path;
                reader >> object_group_path;

                mc::dict interfaces;
                reader >> interfaces;
                
                bool is_object_group = interfaces.find("bmc.kepler.ObjectGroup") != interfaces.end();
                if (is_object_group) {
                    std::lock_guard<std::mutex> lock(m_signal_queue_mutex);    
                    do_request_topology(object_group_path.str());
                }
            } catch (const std::exception& e) {
                elog("failed to handle discovery signal: ${error}", ("error", e.what()));
            }
        });
        
        ilog("discovery signal listening setup completed");
    } catch (const std::exception& e) {
        elog("failed to setup discovery signal listening: ${error}", ("error", e.what()));
    }
}

void topology_discovery::fetch_objects() {
    std::lock_guard<std::mutex> lock(m_signal_queue_mutex);
    if (!is_service_valid()) {
        return;
    }
    
    try {
        // 调用GetManagedObjects获取现有对象
        mc::variant result = m_service->timeout_call(
            mc::milliseconds(TOPOLOGY_DISCOVERY_TIMEOUT_MS), 
            "bmc.kepler.hwdiscovery",
            "/bmc/kepler/ObjectGroup", 
            "org.freedesktop.DBus.ObjectManager",
            "GetManagedObjects", 
            "", 
            mc::variants{}
        );
        
        // 处理返回结果 - GetManagedObjects 返回字典格式: {path: {interface: {properties}}}
        auto managed_objects = result.as_dict();
        wlog("managed_objects: ${managed_objects}", ("managed_objects", managed_objects));
        
        // 将字典转换为vector并按字典序排序
        std::vector<std::pair<std::string, mc::variant>> sorted_objects;
        for (const auto& object_entry : managed_objects) {
            sorted_objects.emplace_back(object_entry.key.as_string(), object_entry.value);
        }
        
        // 按object_path进行字典序排序
        std::sort(sorted_objects.begin(), sorted_objects.end(),
                  [](const auto& a, const auto& b) {
                      return a.first < b.first;
                  });
        
        // 遍历排序后的对象
        for (const auto& object_entry : sorted_objects) {
            if (!is_service_valid()) {
                break; // 如果服务已失效，中断处理
            }
            
            const std::string& object_path = object_entry.first;
            auto interfaces = object_entry.second.as_dict();
            
            bool is_object_group = interfaces.find("bmc.kepler.ObjectGroup") != interfaces.end();
            if (is_object_group) {
                do_request_topology(object_path);
            }
        }
        
        ilog("existing objects fetch completed");
    } catch (const std::exception& e) {
        elog("failed to fetch objects: ${error}", ("error", e.what()));
    }
}

void topology_discovery::do_request_topology(const std::string& object_group_path) {
    if (!is_service_valid()) {
        return;
    }
    
    std::lock_guard<std::mutex> lock(m_topology_mutex);  // 保证顺序执行
    
    // 再次检查服务有效性（可能在等待锁的过程中失效）
    if (!is_service_valid()) {
        return;
    }
    
    try {
        ilog("start processing request topology for path: ${path}", ("path", object_group_path));

        mc::variant topo_result = m_service->timeout_call(
            mc::milliseconds(TOPOLOGY_DISCOVERY_TIMEOUT_MS),
            "bmc.kepler.hwdiscovery",
            object_group_path.c_str(),
            "bmc.kepler.ObjectGroup",
            "GetTopology", 
            "a{ss}",
            mc::variants{mc::dict{}}
        );

        std::string topo_str = topo_result.as_string();
        mc::variant topo_data;
        
        if (topo_str.empty()) {
            ilog("no topology data for path: ${path}", ("path", object_group_path));
            return;
        }
        
        try {
            auto unpacked_data = mc::dbus::serialize::deserialize(topo_str);
            if (unpacked_data.empty()) {
                ilog("no topology data after unpacking for path: ${path}", ("path", object_group_path));
                return;
            }
            
            topo_data = unpacked_data[0];
            
            if (!topo_data.is_dict()) {
                ilog("topology data is not a dictionary for path: ${path}", ("path", object_group_path));
                return;
            }
            
        } catch (const std::exception& e) {
            elog("failed to deserialize topology data for path ${path}: ${error}",
                 ("path", object_group_path)("error", e.what()));
            return;
        }

        // 再次检查服务有效性（在长时间操作前）
        if (!is_service_valid()) {
            return;
        }

        mc::variant objects_result = m_service->timeout_call(
            mc::milliseconds(TOPOLOGY_DISCOVERY_TIMEOUT_MS), 
            "bmc.kepler.hwdiscovery",
            object_group_path.c_str(), 
            "bmc.kepler.ObjectGroup",
            "GetObjects", 
            "a{ss}s", 
            mc::variants{mc::dict{}, "hwproxy"}
        );
        
        // 解析GetObjects的返回值，包含position、objects、life_cycle三个部分
        auto result_array = objects_result.as_array();
        
        if (result_array.size() < 3) {
                    MC_THROW(mc::invalid_arg_exception, "GetObjects returned data format error, expected 3 elements, actual ${actual}", 
                 ("actual", result_array.size()));
        }
        
        // 提取position、objects、life_cycle
        std::string position = result_array[0].as_string();
        mc::variant objects_list = result_array[1];
        mc::variant life_cycle = result_array[2];
        
        mc::mutable_dict combined_objects;
        auto objects_array = objects_list.as_array();
        
        for (const auto& obj : objects_array) {
            auto obj_array = obj.as_array();
            // 每个对象是数组格式：[类名, 对象名, 对象属性, 扩展属性]
            if (obj_array.size() >= 3) {
                std::string class_name = obj_array[0].as_string();
                std::string object_name = obj_array[1].as_string();
                mc::variant properties = mc::json::json_decode(obj_array[2].as_string());
                
                combined_objects[object_name] = properties;
            }
        }

        device_topo::get_instance().add_topology(topo_data.get_object(), combined_objects, position);

        ilog("finish processing request topology for path: ${path}", ("path", object_group_path));
    } catch (const std::exception& e) {
        elog("request topology failed for path ${path}: ${error}",
             ("path", object_group_path)("error", e.what()));
    }
}

} // namespace devmon
